import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { X, Pause, Play } from "lucide-react";
import clsx from "clsx";
import { useTranslation } from "react-i18next";

import { useSelectionStore } from "@/stores/selectionStore";
import { copyToClipboard } from "@/utils";
import platformAdapter from "@/utils/platformAdapter";
import HeaderToolbar from "@/components/Selection/HeaderToolbar";
import type { ActionConfig } from "@/components/Settings/Advanced/components/Selection/config";
import { show_coco } from "@/commands";

// Simple animated selection window content
export default function SelectionWindow() {
  const { t } = useTranslation();
  const [text, setText] = useState("");
  const [visible, setVisible] = useState(false);
  const [animatingOut, setAnimatingOut] = useState(false);
  const containerRef = useRef<HTMLDivElement>(null);
  const [copied, setCopied] = useState(false);
  const [isSpeaking, setIsSpeaking] = useState(false);
  const [isPaused, setIsPaused] = useState(false);
  const [volume, setVolume] = useState(1);
  const utteranceRef = useRef<SpeechSynthesisUtterance | null>(null);
  const voicesRef = useRef<SpeechSynthesisVoice[]>([]);
  const textRef = useRef<string>("");

  const AUTO_HIDE_KEY = "selection_auto_hide_ms";
  const autoHideMs = useMemo(() => {
    const v = Number(localStorage.getItem(AUTO_HIDE_KEY));
    return Number.isFinite(v) && v > 0 ? v : 5000;
  }, []);
  const timerRef = useRef<number | null>(null);

  const scheduleAutoHide = () => {
    if (timerRef.current) {
      clearTimeout(timerRef.current);
    }
    timerRef.current = window.setTimeout(() => {
      close();
    }, autoHideMs);
  };

  useEffect(() => {
    try {
      const updateVoices = () => {
        voicesRef.current = window.speechSynthesis.getVoices();
      };
      updateVoices();
      window.speechSynthesis.onvoiceschanged = updateVoices;
    } catch {}

    const unlistenPromise = platformAdapter.listenEvent(
      "selection-text",
      async ({ payload }: any) => {
        const incoming =
          typeof payload === "string" ? payload : String(payload?.text ?? "");
        const trimmed = incoming.trim();

        const getCurrentWinSafe = async () => {
          try {
            return await platformAdapter.getCurrentWebviewWindow();
          } catch {
            return null;
          }
        };

        if (!useSelectionStore.getState().selectionEnabled) {
          setVisible(false);
          const win = await getCurrentWinSafe();
          win?.hide();
          return;
        }

        if (!trimmed) {
          setText("");
          textRef.current = ""; // sync ref immediately to avoid stale value
          setVisible(false);
          const win = await getCurrentWinSafe();
          win?.hide();
          return;
        }

        setText(incoming);
        textRef.current = incoming; // sync ref immediately to avoid relying on render
        setAnimatingOut(false);
        setVisible(true);

        if (timerRef.current) {
          clearTimeout(timerRef.current);
          timerRef.current = null;
        }
        scheduleAutoHide();
      }
    );

    return () => {
      unlistenPromise
        .then((fn) => {
          try {
            fn();
          } catch {}
        })
        .catch(() => {});
      if (timerRef.current) {
        clearTimeout(timerRef.current);
        timerRef.current = null;
      }
    };
  }, [autoHideMs]);

  const close = async () => {
    if (timerRef.current) {
      clearTimeout(timerRef.current);
      timerRef.current = null;
    }
    try {
      window.speechSynthesis.cancel();
    } catch {}
    setIsSpeaking(false);
    setIsPaused(false);
    setAnimatingOut(true);
    setTimeout(async () => {
      setVisible(false);
      const win = await platformAdapter.getCurrentWebviewWindow();
      win?.hide();
    }, 150);
  };

  const handleChatAction = useCallback(
    async (action: ActionConfig) => {
      const payloadText = (textRef.current || "").trim();

      if (!payloadText) return;

      await show_coco();
      await new Promise((r) => setTimeout(r, 300));

      await platformAdapter.emitEvent("selection-action", {
        action: "chat",
        text: payloadText,
        assistantId: action.assistantId,
        serverId: action.assistantServerId,
      });

      if (!isSpeaking) {
        await close();
      }
    },
    [isSpeaking, close]
  );

  const searchMain = useCallback(async () => {
    const payloadText = (textRef.current || "").trim();
    console.log("searchMain payload", payloadText);
    if (!payloadText) return;

    await show_coco();
    await new Promise((r) => setTimeout(r, 120));
    await platformAdapter.emitEvent("selection-action", {
      action: "search",
      text: payloadText,
    });
    if (!isSpeaking) {
      await close();
    }
  }, []);

  const stopSpeak = () => {
    window.speechSynthesis.cancel();
    utteranceRef.current = null;
    setIsSpeaking(false);
    setIsPaused(false);
  };

  const speak = useCallback(async () => {
    try {
      const trimmed = text.trim();
      if (!trimmed) return;

      if (isSpeaking && !isPaused) {
        window.speechSynthesis.pause();
        setIsPaused(true);
        return;
      }
      if (isSpeaking && isPaused) {
        window.speechSynthesis.resume();
        setIsPaused(false);
        return;
      }

      const utterance = new SpeechSynthesisUtterance(trimmed);
      const zhVoice =
        voicesRef.current.find((v) => /zh|cn/i.test(v.lang)) ||
        window.speechSynthesis.getVoices().find((v) => /zh|cn/i.test(v.lang));
      if (zhVoice) utterance.voice = zhVoice;
      utterance.rate = 1;
      utterance.volume = volume;

      // pause auto-hide while speaking
      if (timerRef.current) {
        clearTimeout(timerRef.current);
        timerRef.current = null;
      }

      utterance.onend = () => {
        setIsSpeaking(false);
        setIsPaused(false);
        utteranceRef.current = null;
        scheduleAutoHide();
      };
      utterance.onerror = () => {
        setIsSpeaking(false);
        setIsPaused(false);
        utteranceRef.current = null;
        scheduleAutoHide();
      };

      utteranceRef.current = utterance;
      window.speechSynthesis.cancel();
      window.speechSynthesis.speak(utterance);
      setIsSpeaking(true);
      setIsPaused(false);
    } catch (e) {
      console.error("TTS play failed:", e);
      stopSpeak();
      scheduleAutoHide();
    }
  }, [text]);

  const handleCopy = useCallback(async () => {
    const payloadText = (textRef.current || "").trim();
    if (!payloadText) return;

    try {
      await copyToClipboard(payloadText, true);
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch (e) {
      console.warn("Copy failed:", e);
    }
  }, []);

  const getActionHandler = (action: ActionConfig) => {
    switch (action.type) {
      case "ask_ai":
      case "translate":
      case "summary":
        handleChatAction(action);
        break;
      case "copy":
        handleCopy();
        break;
      case "search":
        searchMain();
        break;
      case "speak":
        speak();
        break;
      default:
        break;
    }
  };

  const iconsOnly = useSelectionStore((s) => s.iconsOnly);
  const toolbarConfig = useSelectionStore((s) => s.toolbarConfig);
  const toolbarRef = useRef<HTMLDivElement | null>(null);

  const visibleButtons = useMemo(
    () => (Array.isArray(toolbarConfig) ? toolbarConfig : []),
    [toolbarConfig]
  );

  // Resize window width to fit toolbar content (sum child widths to avoid feedback growth)
  const resizeToContentWidth = useCallback(async () => {
    try {
      if (!visible) return;
      const el = toolbarRef.current;
      if (!el) return;

      // Robust intrinsic width measurement: clone offscreen to avoid flex shrink/grow feedback.
      const clone = el.cloneNode(true) as HTMLElement;
      clone.style.position = "absolute";
      clone.style.visibility = "hidden";
      clone.style.left = "-10000px";
      clone.style.top = "0";
      clone.style.width = "auto";
      clone.style.maxWidth = "none";
      clone.style.overflow = "visible";
      // prevent wrapping that may change inline width
      (clone.style as any).whiteSpace = "nowrap";
      document.body.appendChild(clone);
      const intrinsicWidth = Math.ceil(clone.getBoundingClientRect().width);
      clone.remove();

      // Apply small buffer and clamp by mode
      const PAD = 12;
      const WIDTH_MIN = iconsOnly ? 160 : 240;
      const WIDTH_MAX = iconsOnly ? 640 : 960;
      const desiredWidth = Math.max(
        WIDTH_MIN,
        Math.min(WIDTH_MAX, Math.ceil(intrinsicWidth + PAD))
      );

      const win = await platformAdapter.getCurrentWebviewWindow();
      const size = await win.innerSize();
      // Only update width; preserve current height
      const currentWidth = size.width;
      if (Math.abs(currentWidth - desiredWidth) >= 2) {
        await platformAdapter.setWindowSize(desiredWidth, 32);
      }
    } catch (e) {
      console.warn("resizeToContentWidth failed:", e);
    }
  }, [visible, iconsOnly]);

  // Recalculate on relevant changes (buttons, labels, speaking state, visibility)
  useEffect(() => {
    if (!visible) return;
    // Ensure DOM updated before measure
    const id = window.requestAnimationFrame(() => {
      resizeToContentWidth();
    });
    return () => cancelAnimationFrame(id);
  }, [visibleButtons, iconsOnly, isSpeaking, visible, resizeToContentWidth]);

  // Component: selected text preview
  const TextPreview = ({ text }: { text: string }) => {
    return (
      <div className="relative">
        <div
          data-tauri-drag-region="false"
          className="rounded-md bg-black/5 dark:bg-white/5 px-2 py-1 leading-4 text-[12px] text-ellipsis whitespace-nowrap overflow-hidden"
        >
          {text || t("selection.noText")}
        </div>
        {copied && (
          <div className="pointer-events-none absolute inset-0 flex items-center justify-start pl-2">
            <span className="px-2 py-1 rounded bg-black/75 text-white text-[12px]">
              {t("selection.copied")}
            </span>
          </div>
        )}
      </div>
    );
  };

  // Component: speak controls
  const SpeakControls = () => {
    return (
      <div className="flex items-center gap-1">
        <button
          className="flex items-center gap-1 p-1 rounded-md cursor-pointer whitespace-nowrap transition-all duration-150"
          onClick={stopSpeak}
          title={t("selection.speak.stopTitle")}
          aria-label={t("selection.speak.stopAria")}
        >
          <X className="size-4 transition-transform duration-150" />
          {!iconsOnly && (
            <span className="text-[12px] transition-opacity duration-150">
              {t("selection.speak.stopLabel")}
            </span>
          )}
        </button>
        <button
          className="hidden items-center gap-1 p-1 rounded-md cursor-pointer whitespace-nowrap transition-all duration-150"
          onClick={speak}
          title={
            isPaused
              ? t("selection.speak.resumeTitle")
              : t("selection.speak.pauseTitle")
          }
          aria-pressed={isPaused}
          aria-label={
            isPaused
              ? t("selection.speak.resumeAria")
              : t("selection.speak.pauseAria")
          }
        >
          {isPaused ? (
            <Play className="size-4 transition-transform duration-150" />
          ) : (
            <Pause className="size-4 transition-transform duration-150" />
          )}
          {!iconsOnly && (
            <span className="text-[12px] transition-opacity duration-150">
              {isPaused
                ? t("selection.speak.resumeLabel")
                : t("selection.speak.pauseLabel")}
            </span>
          )}
        </button>
        <label className="hidden items-center gap-1 text-[12px]">
          <span className="sr-only">{t("selection.speak.volumeSr")}</span>
          <input
            type="range"
            min={0}
            max={1}
            step={0.1}
            value={volume}
            onChange={(e) => setVolume(Number(e.target.value))}
            aria-label={t("selection.speak.volumeAria")}
          />
        </label>
      </div>
    );
  };

  return (
    <div
      ref={containerRef}
      onMouseDown={(e) => {
        if (e.target === containerRef.current && !isSpeaking) {
          close();
        }
      }}
      className={clsx(
        "m-0 p-0 w-full h-full overflow-hidden",
        "text-[13px] select-none",
        "bg-white dark:bg-[#1E293B]",
        "text-[#111] dark:text-[#ddd]",
        "rounded-xl",
        "transition-all duration-150",
        {
          "translate-y-0": visible && !animatingOut,
          "translate-y-1": !visible || animatingOut,
        }
      )}
    >
      <div className="hidden px-2 pt-1">
        <TextPreview text={text} />
      </div>

      <HeaderToolbar
        buttons={visibleButtons as any}
        iconsOnly={iconsOnly}
        onAction={getActionHandler}
        onLogoClick={show_coco}
        rootRef={toolbarRef}
      >
        {isSpeaking && <SpeakControls />}
      </HeaderToolbar>
    </div>
  );
}
